#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

#include "c_timer.h"
#include "set.h"

static int intLessThan(const void * l, const void * r) {
	assert(r != NULL && l != NULL);

	return *(int *)l < *(int *)r;
}

static void onErased(void * data) {
	printf("[INF] (%s, %d) Erased: %d\n", __FILE__, __LINE__, *(int *)data);
}

static int testSet(unsigned int times, unsigned int size, unsigned int random, unsigned int detail) {
	Set * set;
	CTimer timer;
	unsigned int time[4] = {0};
	int * data;
	int i;
	SetIterator * iterator;

	set = setNew(intLessThan, NULL);

	cTimerNew(&timer);

	data = (int *)malloc(sizeof(int) * size);
	if (random == 1) {
		for (i = 0; i < size; i++) {
			data[i] = rand() % size;
		}
	}
	else {
		for (i = 0; i < size; i++) {
			data[i] = i;
		}
	}

	while (times--) {
		for (i = 0; i < size; i++) {
			iterator = setInsert(set, (void *)&data[i]);
		}
		setClear(set);

		for (i = 0; i < size; i++) {
			cTimerStart(&timer);
			iterator = setInsert(set, (void *)&data[i]);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != NULL) {
					printf("[INF] (%s, %d) Insert: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
				}
				else {
					printf("[ERR] (%s, %d) Insert error: %d\n", __FILE__, __LINE__, data[i]);
				}
			}
		}

		time[0] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		if (detail == 1) {
			for (iterator = setBegin(set); iterator != setEnd(set); iterator = setIteratorNext(iterator)) {
				printf("[INF] (%s, %d) Data: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
			}
		}
		else if (detail == 2) {
			setPrint(set);
		}
		setPrintSize(set);

		for (i = size - 1; i >= 0; i--) {
			cTimerStart(&timer);
			iterator = setFind(set, (void *)&i);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != NULL) {
					printf("[INF] (%s, %d) Found: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
				}
				else {
					printf("[INF] (%s, %d) Not found: %d\n", __FILE__, __LINE__, i);
				}
			}
		}

		time[1] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		for (i = size - 1; i >= 0; i--) {
			cTimerStart(&timer);
			iterator = setErase(set, (void *)&i);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != NULL) {
					printf("[INF] (%s, %d) Delete: %d\n", __FILE__, __LINE__, i);
				}
				else {
					printf("[ERR] (%s, %d) Delete error: %d\n", __FILE__, __LINE__, i);
				}
			}
		}

		time[2] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		if (detail == 1) {
			for (iterator = setBegin(set); iterator != setEnd(set); iterator = setIteratorNext(iterator)) {
				printf("[INF] (%s, %d) data: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
			}
		}

		printf("[INF] (%s, %d) Insert the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[0] / 1000000);
		printf("[INF] (%s, %d) Search the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[1] / 1000000);
		printf("[INF] (%s, %d) Delete the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[2] / 1000000);
	}

	free(data);

	cTimerDelete(&timer);

	setDelete(set);

	return 0;
}

static int testSetForInt(unsigned int times, unsigned int size, unsigned int random, unsigned int detail) {
	Set * set;
	CTimer timer;
	unsigned int time[4] = {0};
	int * data;
	int i;
	SetIterator * iterator;

	set = setForIntNew();

	cTimerNew(&timer);

	data = (int *)malloc(sizeof(int) * size);
	if (random == 1) {
		for (i = 0; i < size; i++) {
			data[i] = rand() % size;
		}
	}
	else {
		for (i = 0; i < size; i++) {
			data[i] = i;
		}
	}

	while (times--) {
		for (i = 0; i < size; i++) {
			iterator = setForIntInsert(set, data[i]);
		}
		setClear(set);

		for (i = 0; i < size; i++) {
			cTimerStart(&timer);
			iterator = setForIntInsert(set, data[i]);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != NULL) {
					printf("[INF] (%s, %d) Insert: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
				}
				else {
					printf("[ERR] (%s, %d) Insert error: %d\n", __FILE__, __LINE__, data[i]);
				}
			}
		}

		time[0] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		if (detail == 1) {
			for (iterator = setBegin(set); iterator != setEnd(set); iterator = setIteratorNext(iterator)) {
				printf("[INF] (%s, %d) Data: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
			}
		}
		else if (detail == 2) {
			setPrint(set);
		}
		setPrintSize(set);

		for (i = size - 1; i >= 0; i--) {
			cTimerStart(&timer);
			iterator = setForIntFind(set, i);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != setEnd(set)) {
					printf("[INF] (%s, %d) Found: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
				}
				else {
					printf("[INF] (%s, %d) Not found: %d\n", __FILE__, __LINE__, i);
				}
			}
		}

		time[1] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		for (i = size - 1; i >= 0; i--) {
			cTimerStart(&timer);
			iterator = setForIntErase(set, i);
			cTimerPause(&timer);
			if (detail == 1) {
				if (iterator != NULL) {
					printf("[INF] (%s, %d) Delete: %d\n", __FILE__, __LINE__, i);
				}
				else {
					printf("[ERR] (%s, %d) Delete error: %d\n", __FILE__, __LINE__, i);
				}
			}
		}

		time[2] = cTimerGetChange(&timer);
		cTimerReset(&timer);

		if (detail == 1) {
			for (iterator = setBegin(set); iterator != setEnd(set); iterator = setIteratorNext(iterator)) {
				printf("[INF] (%s, %d) data: %d\n", __FILE__, __LINE__, *(int *)setIteratorData(iterator));
			}
		}

		printf("[INF] (%s, %d) Insert the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[0] / 1000000);
		printf("[INF] (%s, %d) Search the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[1] / 1000000);
		printf("[INF] (%s, %d) Delete the %d data spend: %f\n", __FILE__, __LINE__, size, (float)time[2] / 1000000);
	}

	free(data);

	cTimerDelete(&timer);

	setDelete(set);

	return 0;
}

int main(int argc, char * argv[]) {
	unsigned int use = 0;
	unsigned int times = 0;
	unsigned int size = 0;
	unsigned int random = 0;
	unsigned int detail = 0;

	if (argc == 6) {
		use = atoi(argv[1]);
		times = atoi(argv[2]);
		size = atoi(argv[3]);
		random = atoi(argv[4]);
		detail = atoi(argv[5]);
	}
	else {
		printf("[USG] %s %s\n", argv[0], "[0: list, 1: set, 2: set for int] [int: times] [int: data size] [int: random] [int: show detail]");
		return -1;
	}

	switch (use) {
	case 0:
		return -1;
	case 1:
		return testSet(times, size, random, detail);
	case 2:
		return testSetForInt(times, size, random, detail);
	default:
		return -1;
	}
}
